#include "dd_man.h"

#include "dd_line.h"
#include "dd_sphere.h"
#include "dd_box.h"
#include "dd_spline2.h"
#include <cassert>

#include "graphics/graphics_util.h"
#include "util/common_macros.h"		// macros
#include "util/log.h"		// macros
#include "util/mmgr.h"		// Needs to come in early


using namespace dd;

Manager& Manager::Instance()
{
    static Manager Inst;
    return Inst;
}


Manager::Manager()
{
	memset(&m_Buffer3D, 0, sizeof(m_Buffer3D));
	memset(&m_Buffer3D, 0, sizeof(m_Buffer2D));
    Init();
}

Manager::~Manager()
{
    Shutdown();
}

bool Manager::Init()
{
    // Reset the data streams
	Reset();

	//	uncomment this for test data:
/*	static 	math::Spline			TestSpline;
	TestSpline.AddPoint(math::Vec3(0,0,10));
	TestSpline.AddPoint(math::Vec3(10,20,20));
	TestSpline.AddPoint(math::Vec3(20,-20,30));
	TestSpline.AddPoint(math::Vec3(30,30,50));
	TestSpline.AddPoint(math::Vec3(40,-30,80));
	TestSpline.AddPoint(math::Vec3(50,40,120));
	TestSpline.AddPoint(math::Vec3(60,-50,200));
	TestSpline.AddPoint(math::Vec3(70,60,0));
	AddSpline(&TestSpline, 0.2f, ColorRGBA(0, 0, 255));

	AddBox(math::Vec3(-100, -100, -100), math::Vec3(100, 100, 100), ColorRGBA(0, 255, 0));

	math::Plane P;
	P.PlaneFromPoint(math::Vec3(0,0,0), math::Vec3(0, 1, 0));
	AddPlane(math::Vec3(0, 0, 0), math::Vec3(1000, 000, 1000), P);

	AddLine(math::Vec3(0, 0, 0), math::Vec3(0, 100, 0), ColorRGBA(255, 0, 0), true);
	AddLine(math::Vec3(0, 0, 0), math::Vec3(100, 0, 0), ColorRGBA(0, 255, 0), true);
	AddLine(math::Vec3(0, 0, 0), math::Vec3(0, 0, 100), ColorRGBA(0, 0, 255), true);

	AddSphere(math::Vec3(0, 100, 0), 200, true, ColorRGBA(58, 200, 58, 128));
*/


	return true;
}

bool Manager::Shutdown()
{
	UTIL_SAFE_DELETEA(m_Buffer3D.DataA);
	UTIL_SAFE_DELETEA(m_Buffer2D.DataA);
	return true;
}

bool Manager::Render3D()
{
	m_Buffer3D.CurrentVertC = 0;
	m_Buffer3D.MaxVertC = 3000;
	m_Buffer3D.pGraphics = GetGraphics();
	if (!m_Buffer3D.DataA)
	{
		m_Buffer3D.DataA = new Graphics::Vertex3D[m_Buffer3D.MaxVertC];
		memset(m_Buffer3D.DataA, 0, sizeof(Graphics::Vertex3D) * m_Buffer3D.MaxVertC);
	}


    math::Mat4x4    Ident;
	Ident.Identity();
	m_Buffer3D.pGraphics->SetTransform(Graphics::TS_WORLD, Ident);
	m_Buffer3D.pGraphics->SetTexture(0, NULL);


	m_Buffer3D.pGraphics->SetRenderState(Graphics::RS_ZREAD,		true);
	m_Buffer3D.pGraphics->SetRenderState(Graphics::RS_ZWRITE,		true);
	m_Buffer3D.pGraphics->SetRenderState(Graphics::RS_LIGHTING,	false);
	m_Buffer3D.pGraphics->SetRenderState(Graphics::RS_ALPHA,		true);
	m_Buffer3D.pGraphics->SetRenderState(Graphics::RS_SRC_BLEND, Graphics::RV_BLEND_SRC_ALPHA);
	m_Buffer3D.pGraphics->SetRenderState(Graphics::RS_DST_BLEND, Graphics::RV_BLEND_INVSRC_ALPHA);
	m_Buffer3D.pGraphics->SetTexStage( 0, Graphics::COLOR_OP,   Graphics::BLEND_SELECTARG1);
	m_Buffer3D.pGraphics->SetTexStage( 0, Graphics::COLOR_ARG1, Graphics::SV_DIFFUSE);
	m_Buffer3D.pGraphics->SetTexStage( 0, Graphics::COLOR_ARG2, Graphics::SV_TEXTURE);
	m_Buffer3D.pGraphics->SetTexStage( 0, Graphics::ALPHA_OP,   Graphics::BLEND_SELECTARG1);
    m_Buffer3D.pGraphics->SetTexStage( 0, Graphics::ALPHA_ARG1, Graphics::SV_DIFFUSE);
    m_Buffer3D.pGraphics->SetTexStage( 0, Graphics::ALPHA_ARG2, Graphics::SV_TEXTURE);
	m_Buffer3D.pGraphics->SetTexStage( 1, Graphics::COLOR_OP,   Graphics::BLEND_DISABLE);

	for (int i=0; i<m_UsedA[Line]; i++)
		m_LineA[i].Render(&m_Buffer3D);

	for (int i=0; i<m_UsedA[Box]; i++)
		m_BoxA[i].Render(&m_Buffer3D);


	for (int i=0; i<m_UsedA[Sphere]; i++)
		m_SphereA[i].Render(&m_Buffer3D);

	for (int i=0; i<m_UsedA[OBB]; i++)
		m_OBBA[i].Render(&m_Buffer3D);

	for (int i=0; i<m_UsedA[Plane]; i++)
		m_PlaneA[i].Render(&m_Buffer3D);


	// Render the remainder
	m_LineA[0].FlushBuffer(&m_Buffer3D);	

//	Reset();
	return true;
}

bool Manager::Render2D()
{
	m_Buffer2D.CurrentVertC = 0;
	m_Buffer2D.MaxVertC = 3000;
	m_Buffer2D.pGraphics = GetGraphics();
	m_Buffer2D.Width = (float)m_Buffer2D.pGraphics->GetScreenWidth();
	m_Buffer2D.Height = (float)m_Buffer2D.pGraphics->GetScreenHeight();
	if (!m_Buffer2D.DataA)
	{
		m_Buffer2D.DataA = new Graphics::Vertex2D[m_Buffer2D.MaxVertC];
		memset(m_Buffer2D.DataA, 0, sizeof(Graphics::Vertex2D) * m_Buffer2D.MaxVertC);
	}


    math::Mat4x4    Ident;
	Ident.Identity();
	m_Buffer2D.pGraphics->SetTransform(Graphics::TS_WORLD, Ident);
	m_Buffer2D.pGraphics->SetTexture(0, NULL);


	m_Buffer2D.pGraphics->SetRenderState(Graphics::RS_ZREAD,		false);
	m_Buffer2D.pGraphics->SetRenderState(Graphics::RS_ZWRITE,		false);
	m_Buffer2D.pGraphics->SetRenderState(Graphics::RS_LIGHTING,		false);
	m_Buffer2D.pGraphics->SetRenderState(Graphics::RS_ALPHA,		true);
	m_Buffer2D.pGraphics->SetRenderState(Graphics::RS_SRC_BLEND, Graphics::RV_BLEND_SRC_ALPHA);
	m_Buffer2D.pGraphics->SetRenderState(Graphics::RS_DST_BLEND, Graphics::RV_BLEND_INVSRC_ALPHA);
	m_Buffer2D.pGraphics->SetTexStage( 0, Graphics::COLOR_OP,   Graphics::BLEND_SELECTARG1);
	m_Buffer2D.pGraphics->SetTexStage( 0, Graphics::COLOR_ARG1, Graphics::SV_DIFFUSE);
	m_Buffer2D.pGraphics->SetTexStage( 0, Graphics::COLOR_ARG2, Graphics::SV_TEXTURE);
	m_Buffer2D.pGraphics->SetTexStage( 0, Graphics::ALPHA_OP,   Graphics::BLEND_SELECTARG1);
    m_Buffer2D.pGraphics->SetTexStage( 0, Graphics::ALPHA_ARG1, Graphics::SV_DIFFUSE);
    m_Buffer2D.pGraphics->SetTexStage( 0, Graphics::ALPHA_ARG2, Graphics::SV_TEXTURE);
	m_Buffer2D.pGraphics->SetTexStage( 1, Graphics::COLOR_OP,   Graphics::BLEND_DISABLE);

	for (int i=0; i<m_UsedA[Line2D]; i++)
		m_Line2DA[i].Render(&m_Buffer2D);

	for (int i=0; i<m_UsedA[Spline2]; i++)
		m_Spline2A[i].Render(&m_Buffer2D);

	for (int i=0; i<m_UsedA[Box2D]; i++)
		m_Box2DA[i].Render(&m_Buffer2D);

	for (int i=0; i<m_UsedA[Circle2D]; i++)
		m_Circle2DA[i].Render(&m_Buffer2D);


	// Render the remainder
	m_Line2DA[0].FlushBuffer(&m_Buffer2D);	

	Reset();
	return true;
}


bool Manager::Reset()
{
	for (int i=0; i<PRIMITIVE_COUNT; i++)
	{
		m_UsedA[i] = 0;
	}
	return true;
}

void Manager::AddSphere(const math::Vec3& _Pos, float _Radius, bool _DrawInverse, COLOR _Color)
{
	assert(m_UsedA[Sphere] < SPHERE_COUNT);	// forgot to reset ?

	m_SphereA[m_UsedA[Sphere]++].Setup(_Pos, _Radius, _DrawInverse, _Color);
}

void Manager::AddBox(const math::Vec3& _Min, const math::Vec3& _Max, COLOR _Color)
{
	assert(m_UsedA[Box] < BOX_COUNT);	// forgot to reset ?
	m_BoxA[m_UsedA[Box]++].Setup(_Min, _Max, _Color);
}

void Manager::AddLine(const math::Vec3& _From, const math::Vec3& _To, COLOR _Color, bool _Arrow )
{
	assert(m_UsedA[Line] < LINE_COUNT);	// forgot to reset ?

	m_LineA[m_UsedA[Line]++].Setup(_From, _To, _Color, _Arrow);
}


void Manager::AddSpline2(math::SplineCache2D* _pSpline, float dT , COLOR _Color, bool _Knots, const math::Vec2& Offset)
{
	assert(m_UsedA[Spline2] < SPLINE2_COUNT);	// forgot to reset ?

	m_Spline2A[m_UsedA[Spline2]++].Setup(_pSpline, dT, _Color, _Knots, Offset);
}


void Manager::AddPlane(const math::Vec3& _Pos, const math::Vec3& _Size, const math::Plane& _Plane, COLOR _Color, bool _RenderSolid)
{
	assert(m_UsedA[Plane] < PLANE_COUNT);	// forgot to reset ?

	m_PlaneA[m_UsedA[Plane]++].Setup(_Pos, _Size, _Plane, _Color, _RenderSolid);
}

void Manager::AddOBB(const math::Vec3& _Size, const math::Mat4x4& _Mat, COLOR _Color)
{
	assert(m_UsedA[OBB] < OBB_COUNT);	// forgot to reset ?

	m_OBBA[m_UsedA[OBB]++].Setup(_Size, _Mat, _Color);
}

void Manager::AddLine2D(const math::Vec2& _From, const math::Vec2& _To, COLOR _Color, bool _Arrow )
{
	assert(m_UsedA[Line2D] < LINE_2D_COUNT);	// forgot to reset ?

	if (_From.x < 0 && _To.x < 0 || _From.x > 1  && _To.x > 1 || 
		_From.y < 0 && _To.y < 0 || _From.y > 1  && _To.y > 1) return;

	m_Line2DA[m_UsedA[Line2D]++].Setup(_From, _To, _Color, _Arrow);
}

void Manager::AddBox2D(const math::Vec2& _From, const math::Vec2& _To, COLOR _Color , bool IsFilled)
{
	assert(m_UsedA[Box2D] < BOX_2D_COUNT);	// forgot to reset ?

	if (_From.x < 0 && _To.x < 0 || _From.x > 1  && _To.x > 1 || 
		_From.y < 0 && _To.y < 0 || _From.y > 1  && _To.y > 1) return;

	m_Box2DA[m_UsedA[Box2D]++].Setup(_From, _To, _Color, IsFilled);
}

void	Manager::AddCircle2D(const math::Vec2& Pos, float Rad, COLOR _Color, bool IsFilled )
{
	assert(m_UsedA[Circle2D] < CIRCLE_2D_COUNT);	// forgot to reset ?

	if (Pos.x + Rad < 0 || Pos.y + Rad < 0 || Pos.x - Rad > 1 || Pos.y - Rad > 1) return;

	m_Circle2DA[m_UsedA[Circle2D]++].Setup( Pos, Rad, _Color, IsFilled);
}

void	Manager::AddCapsule2D(const math::Vec2& From, const math::Vec2& To, float Rad, COLOR Color )
{
	AddCircle2D(From, Rad, Color);
	AddCircle2D(To, Rad, Color);

	math::Vec2	Normal(To.y-From.y, -(To.x-From.x));
	Normal.Normalize();
	Normal*= Rad;
	Normal.x *= GraphicsUtil::H2W;
	AddLine2D(From+Normal, To+Normal, Color);
	AddLine2D(From-Normal, To-Normal, Color);
}


